class Page {
  /**
   * 构造函数，初始化
   * @param  {Object} canvas 节点
   * @author Jiangzhiwei
   * @time   2021-12-03T23:08:47+0800
   */
  constructor (canvas,
    {
      angle = 30, duration = 1500, foregroundColor = '#fff', backgroundColor = '#eee', borderColor = '#000',
      backgroundImage, borderWidth = 1, bendRatio = 0.1, zIndex = 1000, callback
    }) {
    this.canvas = canvas
    this.cacheCanvas = document.createElement('canvas')
    this.ctx = canvas.getContext('2d')
    this.cacheCtx = this.cacheCanvas.getContext('2d')

    this.ratio = 2
    this.isClear = true
    this.pointA = {}
    this.pointB = {}
    this.pointC = {}
    this.pointD = {}
    this.pointE = {}

    this.angle = (angle + 0.5) * Math.PI / 180 // 翻页角度
    this.duration = duration // 翻页持续时间
    this.foregroundColor = foregroundColor // 前景色
    this.backgroundColor = backgroundColor // 背景色
    this.backgroundImage = backgroundImage // 背景图片
    this.bendRatio = bendRatio // 折叠比率
    this.borderWidth = borderWidth * this.ratio // 线宽
    this.zIndex = zIndex
    this.callback = callback

    this.resize()
    this.interval = setInterval(() => {
      this.resize()
    }, 1000)
  }

  /**
   * 判断canvas大小是否发生变化
   * @return {Boolean} 是否变化
   * @author Jiangzhiwei
   * @time   2021-12-03T22:48:39+0800
   */
  checkIfSizeChanged () {
    const st = window.getComputedStyle(this.canvas, null)
    const width = Number.parseFloat(st.width)
    const height = Number.parseFloat(st.height)
    if (this.width !== width || this.height !== height) {
      this.width = width
      this.height = height
      return true
    } else {
      return false
    }
  }

  /**
   * 重新调整画布大小
   * @author Jiangzhiwei
   * @time   2021-12-03T23:03:30+0800
   */
  resize () {
    if (this.checkIfSizeChanged()) { // 如果发生了变化
      this.canvasWidth = this.width * this.ratio
      this.canvasHeight = this.height * this.ratio
      const width = this.canvasWidth
      const height = this.canvasHeight
      this.canvas.width = width
      this.canvas.height = height
      this.cacheCanvas.width = width
      this.cacheCanvas.height = height
    }
  }

  /**
   * 释放
   * @author Jiangzhiwei
   * @time   2021-12-03T23:05:46+0800
   */
  dispose () {
    clearInterval(this.interval)
  }

  start () {
    if (this.isRunning) {
      return
    }
    this.isRunning = true
    this.isClear = false
    const width = this.canvasWidth
    const height = this.canvasHeight
    const angle = Math.atan(width / height) - this.angle // 中间的角度
    this.l = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2)) // 对角线长度
    this.len = this.l * Math.cos(angle) // 关键点移动总长度
    this.step = this.len / this.duration // 步长
    this.bendLen = this.bendRatio * this.len // 弯曲长度
    this.s = 0 // 位置
    this.canvas.parentNode.style.zIndex = this.zIndex
    this.run()
  }

  run () {
    const t1 = new Date().getTime()
    this.calculate()
    this.paint()
    if (this.s <= this.len) {
      requestAnimationFrame(() => {
        const t2 = new Date().getTime()
        this.diff = this.step * (t2 - t1)
        this.s += this.diff
        this.run()
      })
    } else {
      this.isRunning = false
      if (typeof this.callback === 'function') {
        this.callback(this)
      } else {
        this.clear()
      }
    }
  }

  paint () {
    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
    this.cacheCtx.clearRect(0, 0, this.cacheCanvas.width, this.cacheCanvas.height)
    this.draw()
    this.ctx.drawImage(this.cacheCanvas, 0, 0, this.canvas.width, this.canvas.height)
  }

  draw () {
    this.cacheCtx.lineWidth = this.borderWidth
    this.cacheCtx.strokeStyle = this.borderColor
    this.cacheCtx.fillStyle = this.backgroundColor
    // 填充前景
    if (this.pointD.x < this.pointA.x || this.pointD.y < this.pointA.y) {
      this.cacheCtx.beginPath()
      this.cacheCtx.moveTo(this.pointD.x, this.pointD.y) // 顶点
      this.cacheCtx.lineTo(this.pointF.x, this.pointF.y) // 左侧点
      this.cacheCtx.quadraticCurveTo(this.pointH.x, this.pointH.y, this.pointL.x, this.pointL.y)
      this.cacheCtx.lineTo(this.pointM.x, this.pointM.y)
      this.cacheCtx.quadraticCurveTo(this.pointI.x, this.pointI.y, this.pointG.x, this.pointG.y)
      this.cacheCtx.closePath()
      this.cacheCtx.fill()
    }
    // 填充背景
    this.cacheCtx.fillStyle = this.foregroundColor
    this.cacheCtx.beginPath()
    this.cacheCtx.moveTo(this.pointL.x, this.pointL.y)
    this.cacheCtx.quadraticCurveTo(this.pointB.x, this.pointB.y, this.pointJ.x, this.pointJ.y)
    this.cacheCtx.lineTo(this.canvasWidth, this.canvasHeight)
    this.cacheCtx.lineTo(this.pointK.x, this.pointK.y)
    this.cacheCtx.quadraticCurveTo(this.pointC.x, this.pointC.y, this.pointM.x, this.pointM.y)
    this.cacheCtx.closePath()
    this.cacheCtx.fill()
    if (this.backgroundImage) { // 存在图片，则绘制图片
      const img = this.backgroundImage
      this.cacheCtx.save()
      this.cacheCtx.clip()
      this.cacheCtx.drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight, 0, 0, this.canvasWidth, this.canvasHeight)
      this.cacheCtx.restore()
    }
    // 描边
    this.cacheCtx.beginPath()
    this.cacheCtx.moveTo(this.pointD.x, this.pointD.y)
    this.cacheCtx.lineTo(this.pointF.x, this.pointF.y)
    this.cacheCtx.quadraticCurveTo(this.pointH.x, this.pointH.y, this.pointL.x, this.pointL.y)
    this.cacheCtx.quadraticCurveTo(this.pointB.x, this.pointB.y, this.pointJ.x, this.pointJ.y)
    this.cacheCtx.quadraticCurveTo(this.pointB.x, this.pointB.y, this.pointL.x, this.pointL.y)
    this.cacheCtx.lineTo(this.pointM.x, this.pointM.y) // 左边点
    this.cacheCtx.quadraticCurveTo(this.pointC.x, this.pointC.y, this.pointK.x, this.pointK.y)
    this.cacheCtx.quadraticCurveTo(this.pointC.x, this.pointC.y, this.pointM.x, this.pointM.y)
    this.cacheCtx.quadraticCurveTo(this.pointI.x, this.pointI.y, this.pointG.x, this.pointG.y)
    this.cacheCtx.lineTo(this.pointD.x, this.pointD.y)
    this.cacheCtx.closePath()
    this.cacheCtx.stroke()
  }

  /**
   * 清空画布
   * @author Jiangzhiwei
   * @time   2021-12-04T23:35:46+0800
   */
  clear () {
    this.isClear = true
    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
    this.cacheCtx.clearRect(0, 0, this.cacheCanvas.width, this.cacheCanvas.height)
    this.canvas.parentNode.style.zIndex = ''
  }

  /**
   * 计算
   * @author Jiangzhiwei
   * @time   2021-12-04T23:34:00+0800
   */
  calculate () {
    this.calcBottomTriangle()
    this.calcK()
    this.calcTopTriangle()
  }

  /**
   * 计算下方三角形相关的三个点
   * @author Jiangzhiwei
   * @time   2021-12-04T18:04:57+0800
   */
  calcBottomTriangle () {
    const width = this.canvasWidth
    const height = this.canvasHeight
    this.pointA.x = width - this.s * Math.sin(this.angle)
    this.pointA.y = height - this.s * Math.cos(this.angle)
    this.pointB.x = width - this.s / Math.sin(this.angle)
    if (this.pointB.x < 0) { // 到达最左边
      this.pointB.y = height + this.pointB.x * Math.tan(this.angle)
      this.pointB.x = 0
    } else {
      this.pointB.y = height
    }
    this.pointC.y = height - this.s / Math.cos(this.angle)
    if (this.pointC.y < 0) { // 到达最上面
      this.pointC.x = width + this.pointC.y / Math.tan(this.angle)
      this.pointC.y = 0
    } else {
      this.pointC.x = width
    }
  }

  /**
   * 计算斜率
   * @author Jiangzhiwei
   * @time   2021-12-04T18:16:32+0800
   */
  calcK () {
    if (this.angle === 0) {
      this.k1 = 0
      this.k2 = null
    } else if (this.angle === Math.PI / 4) {
      this.k1 = null
      this.k2 = 0
    } else {
      this.k1 = Math.tan(Math.PI - this.angle * 2)
      this.k2 = -1 / this.k1
    }
    if (this.angle === Math.PI / 2) {
      this.k3 = null
    } else {
      this.k3 = Math.tan(Math.PI - this.angle)
    }
    this.k4 = this.k3
  }

  /**
   * 计算上方三角形相关
   * @author Jiangzhiwei
   * @time   2021-12-04T22:33:33+0800
   */
  calcTopTriangle () {
    const width = this.canvasWidth
    const height = this.canvasHeight
    const s = this.s + (this.s - this.bendLen > 0 ? this.s - this.bendLen : 0) // 弯曲后的长度
    const s2 = this.s + this.bendLen > s ? s : this.s + this.bendLen // 弯曲后到关键点的长度
    this.pointD.x = width - s * Math.sin(this.angle)
    this.pointD.y = height - s * Math.cos(this.angle)
    this.pointE.x = width - s2 * Math.sin(this.angle)
    this.pointE.y = height - s2 * Math.cos(this.angle)
    // 计算直线b
    this.b1 = this.pointD.y - this.k1 * this.pointD.x
    this.b2 = this.pointD.y - this.k2 * this.pointD.x
    this.b3 = this.pointA.y - this.k3 * this.pointA.x
    this.b4 = this.pointE.y - this.k4 * this.pointE.x
    // 计算点
    this.pointF = this.getPointByKB(this.k1, this.b1, this.k4, this.b4)
    this.pointG = this.getPointByKB(this.k2, this.b2, this.k4, this.b4)
    this.pointH = this.getPointByKB(this.k1, this.b1, this.k3, this.b3) // 左控制点
    this.pointI = this.getPointByKB(this.k2, this.b2, this.k3, this.b3) // 右控制点

    const s3 = this.s < this.bendLen ? this.s * 2 : this.s + this.bendLen
    this.pointEE = {
      x: width - s3 * Math.sin(this.angle),
      y: height - s3 * Math.cos(this.angle)
    }
    this.k5 = this.k4
    this.b5 = this.pointEE.y - this.k5 * this.pointEE.x
    this.pointJ = this.getPointByKB(this.k5, this.b5, 0, height) // 左终点
    this.pointK = { x: width, y: this.k5 * width + this.b5 } // 右终点

    this.pointL = { x: (this.pointB.x + this.pointH.x) / 2, y: (this.pointB.y + this.pointH.y) / 2 }
    this.pointM = { x: (this.pointC.x + this.pointI.x) / 2, y: (this.pointC.y + this.pointI.y) / 2 }
  }

  /**
   * 根据两直线计算交点
   * @param  {Number} k1 斜率1
   * @param  {Number} b1 截距1
   * @param  {Number} k2 斜率2
   * @param  {Number} b2 截距2
   * @return {Object} 点
   * @author Jiangzhiwei
   * @time   2021-12-04T22:33:58+0800
   */
  getPointByKB (k1, b1, k2, b2) {
    const x = (b2 - b1) / (k1 - k2)
    const y = k1 * x + b1
    return { x, y }
  }
}

export default Page
